package utils

import (
	"errors"
	"gitee.com/shijun123456/kcgin"
	"github.com/flyaways/pool"
	"github.com/tal-tech/go-zero/core/logx"
	"google.golang.org/grpc"
	"time"
)

//服务发现魔板
//所有的服务发现最后 必须返回 对应的 grpc.ClientConn 信息
type ServiceRegister interface {
	Conn() (*grpc.ClientConn, error)
	Close()
	PutConn()
}

const (
	defaultcacheTimeout = 3600
)

//具体的实现者
type serviceimpl interface {
	//根据服务名称返回 ip:port
	ServiceFindByServiceName(string) (string, error)
}

//缓存实现具体驱动信息
type cacheImpl interface {
	Load(string) (string, error)
	Set(string, string, int) error
}

//默认魔板实现这个方法
type template struct {
	cacheImpl
	serviceimpl
	servicename string
	grpcPool    *pool.GRPCPool
	conn        *grpc.ClientConn
}

func NewServiceRegister(servicename string) ServiceRegister {
	return &template{servicename: servicename}
}
func (t *template) pool(target string) (*grpc.ClientConn, error) {
	options := &pool.Options{
		InitTargets:  []string{target},
		InitCap:      10,
		MaxCap:       50,
		DialTimeout:  time.Second * 5,
		IdleTimeout:  time.Second * 60,
		ReadTimeout:  time.Second * 5,
		WriteTimeout: time.Second * 5,
	}
	p, err := pool.NewGRPCPool(options, grpc.WithInsecure())
	if err != nil {
		return nil, err
	}
	if p == nil {
		return nil, errors.New("连接池句柄出错")
	}
	conn, err := p.Get()
	t.grpcPool = p
	t.conn = conn
	return conn, err
}
func (t *template) Close() {
	t.conn.Close()
	t.grpcPool.Close()
}
func (t *template) PutConn() {
	t.grpcPool.Put(t.conn)
}

//需要内部的处理grpc去包装
func (t *template) Conn() (*grpc.ClientConn, error) {
	//内置的魔板去生成
	var address string
	cache, err := t.cacheImpl.Load(t.servicename)
	if err != nil {
		return nil, err
	}
	if len(cache) > 0 {
		logx.Infof("从缓存中读取信息: %s",address)
		address = cache
	} else {
		target, err := t.ServiceFindByServiceName(t.servicename)
		if err != nil {
			return nil, err
		}
		address = target
		t.cacheImpl.Set(t.servicename, target, kcgin.AppConfig.DefaultInt("cache.timeout", defaultcacheTimeout))
	}
	return t.pool(address)
}

//包装魔板信息 默认提供魔板信息
type ConsulImpl struct {
	t *template
}

func NewConsulImplRegister(impl cacheImpl) ServiceRegister {
	c := new(ConsulImpl)
	c.t = new(template)
	if impl != nil {
		c.t.cacheImpl = impl //可以指向自己 也可以去指向外部注入的 实现者 为了分离注入即可 自己是集体的信息
	} else {
		c.t.cacheImpl = c
	}
	c.t.serviceimpl = c

	//内嵌tempalte 去适配
	//返回t 魔板去处理
	return c.t
}
func (c *ConsulImpl) ServiceFindByServiceName(servicename string) (string, error) {
	//调用封装号的 consul-client
	client := DefaultConsulClient()
	return client.ServiceFindByMicro(servicename)
}

//默认自己封装缓存 读写操作
//默认是自己内部的 redis客户端处理的
func (c *ConsulImpl) Set(key string, val string, expire int) error {
	NewRedisCache().Init().Set(key, val, expire)
	return nil
}
func (c *ConsulImpl) Load(key string) (string, error) {
	return NewRedisCache().Init().Load(key)
}
